package base.thread_JUC.demo03;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 可重入锁（也叫递归锁）,目的是为了解决死锁.
 * 指的是同一线程外层函数获得锁之后，内层递归函数任然能获取该锁的代码，在同一个线程的外层方法获取锁的时候，再进入内层同步方法时会自动获取锁。
 * 也就是说，线程可以进入任何一个它已经拥有的锁所同步着的代码块。（就是说外层同步函数内部调用其它同步函数代码，同一个线程持有的锁是同一把锁，
 * 而不是说，进入外层函数持有的锁和内部调用其它外层函数持有的锁是同一把锁）
 *
 * t1 sendSMS...    t1线程在外层函数获得锁的时候
 * t1 sendMail...   t1在进入内层函数会自动获取锁
 *
 * t2 sendSMS...
 * t2 sendMail...
 *
 * ============================================
 * t3 sendSMS2...
 *
 * t3 sendMail2...
 * t4 sendSMS2...
 *
 * t4 sendMail2...
 */
public class Client {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sendSMS();
        },"t1").start();

        new Thread(()->{
            phone.sendSMS();
        },"t2").start();

        // 主线程睡眠2s
        TimeUnit.SECONDS.sleep(2);
        System.out.println("======================ReentrantLock演示==========================");

        new Thread(phone, "t3").start();
        new Thread(phone, "t4").start();


    }
}

class Phone  implements Runnable{
    public synchronized void sendSMS(){
        System.out.println(Thread.currentThread().getName()+" sendSMS...");
        sendMail();
    }
    public synchronized void sendMail(){
        System.out.println(Thread.currentThread().getName()+" sendMail...");
    }

    //===============================ReentrantLock演示=====================================

    Lock lock = new ReentrantLock();//默认可重入的非公平锁
    @Override
    public void run() {
        sendSMS2();
    }

    public void sendSMS2() {
        lock.lock();
//        lock.lock(); // lock和unlock只要方法配对就是可以重复使用的。
        try {
            System.out.println(Thread.currentThread().getName()+ " sendSMS2...");
            TimeUnit.SECONDS.sleep(2);
            sendMail2();
        }catch (Exception e){

        }finally {
            lock.unlock();
//            lock.unlock();
        }
    }

    public void sendMail2(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+ " sendMail2...");
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }


}
